package top.codechap.ioc.framework.context.support;

import top.codechap.ioc.framework.beans.BeanDefinition;
import top.codechap.ioc.framework.beans.MutablePropertyValues;
import top.codechap.ioc.framework.beans.PropertyValue;
import top.codechap.ioc.framework.beans.factory.support.BeanDefinitionRegistry;
import top.codechap.ioc.framework.beans.factory.xml.XmlBeanDefinitionReader;
import top.codechap.ioc.framework.utils.StringUtils;

import java.lang.reflect.Method;

/**
 * IOC容器具体的子类
 * 用于加载类路径下的Xml格式的配置文件
 */
public class ClassPathXmlApplicationContext extends AbstractApplicationContext{

    public ClassPathXmlApplicationContext(String configLocation) {
        this.configLocation = configLocation;
        //构建解析器对象
        beanDefinitionReader = new XmlBeanDefinitionReader();
        try {
            this.refresh();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //根据bean对象的名称,获取bean对象
    @Override
    public Object getBean(String name) throws Exception {
        //判断对象容器中是否包含指定名称的bean对象,如果包含,直接返回,如果不包含,需要自行创建
        Object obj = singletonObjects.get(name);
        if (obj != null) {
            return obj;
        }
        //自行创建bean对象
        //获取BeanDefinition对象,先获取解析器对象
        BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
        //再获取BeanDefinition对象
        BeanDefinition beanDefinition = registry.getBeanDefinition(name);
        //获取bean信息中的ClassName
        String className = beanDefinition.getClassName();
        //通过反射创建对象
        Class<?> clazz = Class.forName(className);
        Object beanObj = clazz.newInstance();

        //进行依赖注入操作,其实就是把Xml文件中property标签中用到的对象(ref),赋给新创建的对象的对应的属性;
        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
        for (PropertyValue propertyValue : propertyValues) {
            //获取name属性值
            String propertyName = propertyValue.getName();
            //获取value属性
            String value = propertyValue.getValue();
            //获取ref属性
            String ref = propertyValue.getRef();
            if (ref != null) {  //一个property标签中只能有value或者ref中的一个 && !"".equals(ref)
                //获取依赖的bean对象
                Object bean = getBean(ref); //这里使用的是递归操作
                //拼接set方法名
                String methodName = StringUtils.getSetterMethodByFieldName(propertyName);
                //通过反射获取所有的方法对象
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (methodName.equals(method.getName())) {
                        //执行该set方法
                        method.invoke(beanObj,bean);
                    }
                }
            }
            if (value != null && !"".equals(value)) {
                //拼接set方法名
                String methodName = StringUtils.getSetterMethodByFieldName(propertyName);
                //通过反射获取set方法
                Method method = clazz.getMethod(methodName,String.class);
                //执行该set方法
                method.invoke(beanObj,value);
            }
        }

        //在返回beanObj对象之前,将该对象存储到Map容器中
        singletonObjects.put(name,beanObj);
        return beanObj;
    }

    @Override
    public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {
        Object bean = getBean(name);
        if (bean == null) {
            return null;
        }
        return clazz.cast(bean);
    }
}
